/*
// sum 1 2 3 4
[{
	type: 'Operand',
	val: 'sum',
	expr: [{
		type: 'Number',
		val: 1
	}, {
		type: 'Number',
		val: 2
	}, {
		type: 'Number',
		val: 3
	}, {
		type: 'Number',
		val: 4
	}]
}]

// sum sum 1 2 3
[{
	type: 'Operand',
	val: 'sum',
	expr: [{
		type: 'Operand',
		val: 'sum',
		expr: [{
			type: 'Number',
			val: 1
		}, {
			type: 'Number',
			val: 2
		}, {
			type: 'Number',
			val: 3
		}]
	}]
}]

*/

const exper = "sum sum 1 2 3"; // sum 1 2 3 4

const lexer = str => str.split(" ").map(s => s.trim()).filter(s => s.length);

// console.log(lexer(exper))

const Operand = Symbol("Operand")
const Opercode = Symbol("Opercode")

const parser = tokens => {
	let c = 0

	const opercode = () => tokens[c]
	const operand = () => tokens[c++]

	const parseOperand = () => { return { type: Operand, val: Number(operand()) } }
	const parseOpercode = () => {
		let node = { type: Opercode, val: operand(), expr: [] }
		while(opercode()) node.expr.push(parseExpr())
		return node
	}

	const parseExpr = () => /\d/.test(opercode()) ? parseOperand() : parseOpercode()

	return parseExpr()
}

console.log(parser(lexer(exper)))

const transpiler = ast => {
	const opercodeMap = { sum: '+', sub: '-', mul: '*', div: '/' }
	const transpileNode = ast => ast.type === Operand ? transpileNumber(ast) : transpileCode(ast)
	const transpileNumber = ast => Number(ast.val);
	const transpileCode = ast => `(${ ast.expr.map(transpileNode).join(" " + opercodeMap[ast.val] + " ") })`
	return transpileNode(ast)
}

const program = "mul 1 sub 2 sum 3 2 4"

console.log(transpiler(parser(lexer(program))))